package com.onadake.domain.query;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import com.onadake.utils.ValidationUtils;

/**
 * Describes the simple ordering according one column.
 * Template type is the type of the column.
 * 
 * @author radek.hecl
 *
 * @param <ColumnType> type of the column according which order is done
 */
public class Order<ColumnType> {

    /**
     * Builder object.
     *
     * @param <ColumnType> type of the column according which order is done
     */
	public static class Builder<ColumnType> {
		
		/**
		 * Column.
		 */
		private ColumnType column;
				
		/**
		 * Direction.
		 */
		private Direction direction;
		
		/**
		 * Sets the column.
		 * 
		 * @param column column
		 * @return this instance
		 */
		public Builder<ColumnType> setColumn(ColumnType column) {
			this.column = column;
			return this;
		}
				
		/**
		 * Sets the direction.
		 * 
		 * @param direction direction
		 * @return this instance
		 */
		public Builder<ColumnType> setDirection(Direction direction) {
			this.direction = direction;
			return this;
		}
		
		/**
		 * Sets the source.
		 * 
		 * @param source source
		 * @return this instance
		 */
		public Builder<ColumnType> setSource(Order<ColumnType> source) {
			setColumn(source.column);
			setDirection(source.direction);
			return this;
		}
		
		/**
		 * Builds the result object.
		 * 
		 * @return created object
		 */
		public Order<ColumnType> build() {
			return new Order<ColumnType>(this);
		}

	}
	
	/**
	 * Column.
	 */
	private ColumnType column;
		
	/**
	 * Direction.
	 */
	private Direction direction;
	
	/**
	 * Creates new instance.
	 * 
	 * @param builder builder object
	 */
	public Order(Builder<ColumnType> builder) {
		column = builder.column;
		direction = builder.direction;
		guardInvariants();
	}
	
	/**
	 * Guards this object to be consistent. Throws exception if this is not the case.
	 */
	private void guardInvariants() {
		ValidationUtils.guardNotNull(column, "column cannot be null");
		ValidationUtils.guardNotNull(direction, "direction cannot be null");
	}
	
	/**
	 * Returns the column according which the ordering is requested.
	 * 
	 * @return column according which the ordering should be done
	 */
	public ColumnType getColumn() {
		return column;
	}
		
	/**
	 * Returns the direction of ordering.
	 * 
	 * @return direction of ordering
	 */
	public Direction getDirection() {
		return direction;
	}
	
	/**
	 * Static factory method to simplify construction of the ascendant order.
	 * 
	 * @param column column
	 * @return created order
	 */
	public static <T> Order<T> createAscendant(T column) {
		return new Order.Builder<T>().
				setColumn(column).
				setDirection(Direction.ASCENDANT).
				build();
	}

	/**
	 * Static factory method to simplify construction of the descendant order.
	 * 
	 * @param column column
	 * @return created order
	 */
	public static <T> Order<T> createDescendant(T column) {
		return new Order.Builder<T>().
				setColumn(column).
				setDirection(Direction.DESCENDANT).
				build();
	}

	/**
	 * Static factory method to simplify construction.
	 * 
	 * @param column column
	 * @param direction direction
	 * @return created order
	 */
	public static <T> Order<T> create(T column, Direction direction) {
		return new Order.Builder<T>().
				setColumn(column).
				setDirection(direction).
				build();
	}
	
	@Override
	public int hashCode() {
		return HashCodeBuilder.reflectionHashCode(this);
	}
	
	@Override
	public boolean equals(Object obj) {
		return EqualsBuilder.reflectionEquals(this, obj);
	}
	
	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this);
	}

}
